//#define DebugDeserialization

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Reflection;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Data;
using System.Data.Odbc;
using NumericalRecipes;
using FftLib;
using BmgNet;
using StaticUtility;

namespace ObjectLib
{
    /// <summary>
    /// [Serializable]
    /// Pixels chosen for a single polygon.
    /// </summary>
    [Serializable]
    public class PixelTagger: ISerializable
    {
        public int NumPts;
        public bool[,] tagged;
        public bool[,] CreateBool()
        {
            bool[,] output = new bool[tagged.GetLength(0),tagged.GetLength(1)];
            int i,j;
            for(i = 0;i < output.GetLength(0);i++)
                for(j = 0;i < output.GetLength(1);j++)
                    output[i,j] = tagged[i,j];
            return output;
        }
        public int Left;
        public int Right;
        public int Top;
        public int Bottom;
        [NonSerialized]
        public const int CUSHION=20;
        [NonSerialized]
        public PolygonF Owner;
        /*
		[NonSerialized]
		private Bitmap lightMapCache;
		public Bitmap LightMapCache
		{
			get
			{
				if( (lightMapCache!=null) && (lightMapCache.Width==Right-Left+1) && (lightMapCache.Height==Bottom-Top+1) )
					return lightMapCache;
				
				lightMapCache = new Bitmap(Right-Left+1, Bottom-Top+1);
				return lightMapCache;
			}
		}
        */

        public PixelTagger()
        {
            NumPts=0;
            tagged = new bool[0,0];
            Left=0;
            Right=0;
            Top=0;
            Bottom=0;
        }
        public PixelTagger(Circle cir)
        {
            Left = (int)(cir.Center.X-cir.Radius);
            Right = (int)(cir.Center.X+cir.Radius+1);
            Top = (int)(cir.Center.Y-cir.Radius);
            Bottom = (int)(cir.Center.Y+cir.Radius+1);
            NumPts = 0;
            tagged = new bool[Right-Left+1, Bottom-Top+1];
            double rsq = cir.Radius*cir.Radius;
            double x,y, xc=cir.Center.X, yc=cir.Center.Y;
            int i,j;
            for(i=Left; i<=Right; i++)
                for(j=Top; j<=Bottom; j++)
                {
                    x = (double)i-xc;
                    y = (double)j-yc;
                    if( x*x+y*y <= rsq )
                    {
                        tagged[i-Left,j-Top] = true;
                        NumPts++;
                    }
                }
        }
        public PixelTagger(PolygonF owner)
        {
            NumPts=0;
            tagged = new bool[0,0];
            Left=0;
            Right=0;
            Top=0;
            Bottom=0;

            Owner = owner;
        }
        public PixelTagger(SerializationInfo info,StreamingContext context)
        {
#if DebugDeserialization
            ObjectParserData.DbgMsg = "Begin PixelTagger";
#endif
            //	info.Get____
            NumPts = info.GetInt32("NumPts");
            tagged = (bool[,])info.GetValue("Tagged",typeof(bool[,]));
            Left = info.GetInt32("Left");
            Right = info.GetInt32("Right");
            Top = info.GetInt32("Top");
            Bottom = info.GetInt32("Bottom");
#if DebugDeserialization
            ObjectParserData.DbgMsg = "End PixelTagger";
#endif
        }
        public virtual void GetObjectData(SerializationInfo info,StreamingContext context)
        {	//	info.AddValue
            info.AddValue("NumPts",NumPts);
            info.AddValue("Tagged",tagged);
            info.AddValue("Left",Left);
            info.AddValue("Right",Right);
            info.AddValue("Top",Top);
            info.AddValue("Bottom",Bottom);
        }
        public bool AddPixel(int x,int y)
        {
            if(x>=0 && y>=0)
            {
                if(tagged.Length==0)
                {
                    NumPts++;
                    Left = x-CUSHION;
                    Right = x+CUSHION;
                    Top = y-CUSHION;
                    Bottom = y+CUSHION;
                    tagged = new bool[Right-Left+1,Bottom-Top+1];
                    tagged[x-Left,y-Top] = true;
                    return true;
                }
                else
                {
                    if(x>=Left && x<=Right && y>=Top && y<=Bottom)
                    {
                        if(!tagged[x-Left,y-Top])
                        {
                            NumPts++;
                            tagged[x-Left,y-Top] = true;
                            return true;
                        }
                    }
                    else
                    {
                        this.Expand(x,y);
                        return AddPixel(x,y);
                    }
                }
            }
            return false;
        }
        public bool RemovePixel(int x,int y)
        {
            if(x>=Left && x<=Right && y>=Top && y<=Bottom)
            {
                if(tagged[x-Left,y-Top])
                {
                    NumPts--;
                    tagged[x-Left,y-Top] = false;
                    return true;
                }
                if(NumPts==0)
                {
                    Left = 0; Right = 0; Top = 0; Bottom = 0;
                    tagged = new bool[0,0];
                }
            }
            return false;
        }
        public void Expand(int x,int y)
        {
            int width = Right-Left+1;
            int height = Bottom-Top+1;

            int newLeft,newRight,newTop,newBottom;

            newLeft=Left;
            newRight=Right;
            newTop=Top;
            newBottom=Bottom;

            if(x<Left)
                newLeft = x-width;
            else if(x>Right)
                newRight = x+width;
            if(y<Top)
                newTop = y-height;
            if(y>Bottom)
                newBottom = y+height;

            bool[,] newTag = new bool[newRight-newLeft+1,newBottom-newTop+1];
            for(int i=Left;i<=Right;i++)
                for(int j=Top;j<=Bottom;j++)
                    newTag[i-newLeft,j-newTop]=tagged[i-Left,j-Top];

            tagged = newTag;

            Right=newRight;
            Left=newLeft;
            Top=newTop;
            Bottom=newBottom;
        }
        public void Expand(Rectangle newRect)
        {
            if(tagged.Length == 0)
            {
                Left=newRect.Left;
                Right=newRect.Right;
                Top=newRect.Top;
                Bottom=newRect.Bottom;
                tagged = new bool[Right-Left+1,Bottom-Top+1];
            }
            else
            {
                int width = Right-Left+1;
                int height = Bottom-Top+1;

                int newLeft,newRight,newTop,newBottom;

                newLeft=Math.Min(Left,newRect.Left);
                newRight=Math.Max(Right,newRect.Right);
                newTop=Math.Min(Top,newRect.Top);
                newBottom=Math.Max(Bottom,newRect.Bottom);

                bool[,] newTag = new bool[newRight-newLeft+1,newBottom-newTop+1];
                if(tagged.Length > 0)
                {
                    for(int i=Left;i<=Right;i++)
                        for(int j=Top;j<=Bottom;j++)
                            newTag[i-newLeft,j-newTop]=tagged[i-Left,j-Top];
                }

                tagged = newTag;

                Right=newRight;
                Left=newLeft;
                Top=newTop;
                Bottom=newBottom;
            }
        }
        public void ResizeAndClear(Rectangle newRect)
        {
            tagged = new bool[newRect.Width+1,newRect.Height+1];
            Left = newRect.Left;
            Right = newRect.Right;
            Top = newRect.Top;
            Bottom = newRect.Bottom;
            NumPts = 0;
        }
        public void ResizeAndMaintain(Rectangle newRect)
        {
            PixelTagger temp = new PixelTagger();
            temp.ResizeAndClear(newRect);
            int xFrom = Math.Max(newRect.Left,Left);
            int xTo = Math.Min(newRect.Right,Right);
            int yFrom = Math.Max(newRect.Top,Top);
            int yTo = Math.Min(newRect.Bottom,Bottom);
            int x,y;
            for(x=xFrom;x<=xTo;x++)
                for(y=yFrom;y<=yTo;y++)
                    if(this.PixelIsTagged(x,y))
                        temp.AddPixel(x,y);

            NumPts = temp.NumPts;
            Left = temp.Left;
            Right = temp.Right;
            Bottom = temp.Bottom;
            Top = temp.Top;
            tagged = temp.tagged;
        }
        /// <summary>
        /// Sets this instance equal to only those pixels explicitly defined by the set of polygons.
        /// </summary>
        /// <param name="polys"></param>
        public void FromPolygons(PolygonFlist polys)
        {
            if(polys.Count > 0)
            {
                Rectangle rBound = ((PolygonF)polys[0]).GetBoundingRect(0.0f);
                for(int i=1;i<polys.Count;i++) rBound = BoundingRect(rBound,((PolygonF)polys[i]).GetBoundingRect(0.0f));
                ResizeAndClear(rBound);
                PolygonF poly;
                //int horse;

                for(int i=0;i<polys.Count;i++)
                {
                    poly = (PolygonF)polys[i];
                    Rectangle bounding = poly.GetBoundingRect(0.0f);
                    for(int j=bounding.Left;j<=bounding.Right;j++)
                    {
                        for(int k=bounding.Top;k<=bounding.Bottom;k++)
                        {
                            if(!tagged[j-rBound.Left,k-rBound.Top])
                            {
                                if(poly.InPolyNotSwallowed((float)j,(float)k) > 0)
                                {
                                    NumPts++;
                                    tagged[j-rBound.Left,k-rBound.Top]=true;
                                }
                            }
                        }
                    }
                }
            }
        }
        public bool PixelIsTagged(int x,int y)
        {
            if(tagged.Length>0)
                if(x>=Left && x<=Right && y>=Top && y<=Bottom)
                    if(tagged[x-Left,y-Top])
                        return true;
            return false;
        }
        public void AddPoly(PolygonF poly)
        {
            Rectangle r = poly.GetBoundingRect(1.0f);
            Expand(r);
            float x,y;
            for(int i=r.Left;i<=r.Right;i++)
            {
                for(int j=r.Top;j<=r.Bottom;j++)
                {
                    x=(float)i;
                    y=(float)j;
                    if(poly.InPolyNotSwallowed(x,y) > 0)
                        AddPixel(i,j);
                }
            }
        }
        public void AddPoly(PolygonF poly,Rectangle clip)
        {
            Rectangle r = poly.GetBoundingRect(0.0f);
            int left = Math.Max(r.Left,clip.Left);
            int top = Math.Max(r.Top,clip.Top);
            int right = Math.Min(r.Right,clip.Right);
            int bottom = Math.Min(r.Bottom,clip.Bottom);
            r = new Rectangle(left,top,right-left+1,bottom-top+1);
            Expand(r);
            float x,y;
            for(int i=r.Left;i<=r.Right;i++)
            {
                for(int j=r.Top;j<=r.Bottom;j++)
                {
                    x=(float)i;
                    y=(float)j;
                    if(poly.InPolyNotSwallowed(x,y) > 0)
                        AddPixel(i,j);
                }
            }
        }
        public void RemovePoly(PolygonF poly)
        {
            Rectangle r = poly.GetBoundingRect(0.0f);
            float x,y;
            for(int i=r.Left;i<=r.Right;i++)
            {
                for(int j=r.Top;j<=r.Bottom;j++)
                {
                    x=(float)i;
                    y=(float)j;
                    if(poly.InPolyNotSwallowed(x,y) > 0)
                        RemovePixel(i,j);
                }
            }
        }
        public void RemovePoly(PolygonF poly,Rectangle clip)
        {
            Rectangle r = poly.GetBoundingRect(0.0f);
            int left = Math.Max(r.Left,clip.Left);
            int top = Math.Max(r.Top,clip.Top);
            int right = Math.Min(r.Right,clip.Right);
            int bottom = Math.Min(r.Bottom,clip.Bottom);
            r = new Rectangle(left,top,right-left+1,bottom-top+1);
            float x,y;
            for(int i=r.Left;i<=r.Right;i++)
            {
                for(int j=r.Top;j<=r.Bottom;j++)
                {
                    x=(float)i;
                    y=(float)j;
                    if(poly.InPolyNotSwallowed(x,y) > 0)
                        RemovePixel(i,j);
                }
            }
        }
        public void RemovePix(PixelTagger pix)
        {
            int left = Math.Max(this.Left, pix.Left);
            int top = Math.Max(this.Top, pix.Top);
            int right = Math.Min(this.Right, pix.Right);
            int bottom = Math.Min(this.Bottom, pix.Bottom);
            int i,j;
            for(i=left; i<=right; i++)
                for(j=top; j<=bottom; j++)
                {
                    if(pix.PixelIsTagged(i,j))
                        this.RemovePixel(i,j);
                }
        }
        public void AddPix(PixelTagger pix)
        {
            int i,j;
            for(i=pix.Left;i<=pix.Right;i++)
                for(j=pix.Top;j<=pix.Bottom;j++)
                {
                    if(pix.PixelIsTagged(i,j))
                        this.AddPixel(i,j);
                }
        }
        public static Rectangle BoundingRect(Rectangle r1,Rectangle r2)
        {
            int t = r1.Top;
            int b = r1.Bottom;
            int l = r1.Left;
            int r = r1.Right;
            if(t > r2.Top) t = r2.Top;
            if(b < r2.Bottom) b = r2.Bottom;
            if(l > r2.Left) l = r2.Left;
            if(r < r2.Right) r = r2.Right;
            return new Rectangle(l,t,r-l+2,b-t+2);
        }
        public void Clip(Rectangle rect)
        {
            int newLeft,newRight,newTop,newBottom;
            bool clipped = false;

            newLeft=Left;
            newRight=Right;
            newTop=Top;
            newBottom=Bottom;
            if(rect.Left > Left)
            {
                newLeft = rect.Left;
                clipped = true;
            }
            if(rect.Right < Right)
            {
                newRight = rect.Right;
                clipped = true;
            }
            if(rect.Top > Top)
            {
                newTop = rect.Top;
                clipped = true;
            }
            if(rect.Bottom < Bottom)
            {
                newBottom = rect.Bottom;
                clipped = true;
            }
            if(clipped)
                ResizeAndMaintain(new Rectangle(newLeft,newTop,newRight-newLeft,newBottom-newTop));
        }
        public Rectangle TaggedRect
        {
            get
            {
                return new Rectangle(Left,Top,Right-Left+1,Bottom-Top+1);
            }
        }
        public RectangleF TaggedRectF
        {
            get
            {
                return new RectangleF(Left,Top,Right-Left+1,Bottom-Top+1);
            }
        }
        public void SavePngMask(string pngMaskFileName)
        {
            Rectangle rect = this.TaggedRect;
            Bitmap bmp = new Bitmap(rect.Width,rect.Height);
            int i,j;
            for(i=0;i<rect.Width;i++)
                for(j=0;j<rect.Height;j++)
                {
                    if(this.PixelIsTagged(i+rect.Left,j+rect.Top))
                        bmp.SetPixel(i,j,Color.White);
                    else
                        bmp.SetPixel(i,j,Color.Black);
                }
            bmp.Save(pngMaskFileName,ImageFormat.Png);
        }
        public static int _x,_y;
        public Point RandomPointInPix
        {
            get
            {
                if(this.NumPts<=0)
                    return Point.Empty;
                else
                {
                    _x = Left + (int)((double)(Right-Left)*Util.Rand.NextDouble());
                    _y = Top + (int)((double)(Bottom-Top)*Util.Rand.NextDouble());
                    while(!PixelIsTagged(_x,_y))
                    {
                        _x = Left + (int)((double)(Right-Left)*Util.Rand.NextDouble());
                        _y = Top + (int)((double)(Bottom-Top)*Util.Rand.NextDouble());
                    }
                    return new Point(_x,_y);
                }
            }
        }
        public bool FullyContained(Circle cir)
        {
            int rad = (int)(cir.Radius+0.5f);
            int xc = (int)(cir.Center.X+0.5f);
            int yc = (int)(cir.Center.Y+0.5f);
            int i,j,radsq = rad*rad;
            for(i=-rad; i<=rad; i++)
                for(j=-rad; j<=rad; j++)
                    if (!PixelIsTagged(xc+i,yc+j) && i*i+j*j<radsq)
                        return false;
            return true;
        }
        public bool FullyExcluded(Circle cir)
        {
            int rad = (int)(cir.Radius+0.5f);
            int xc = (int)(cir.Center.X+0.5f);
            int yc = (int)(cir.Center.Y+0.5f);
            int i,j,radsq = rad*rad;
            for(i=-rad;i<=rad;i++)
                for(j=-rad;j<=rad;j++)
                    if(PixelIsTagged(xc+i,yc+j) && i*i+j*j<radsq)
                        return false;
            return true;
        }
        public double ProportionOfCircle(Circle cir)
        {
            int ncir=0;
            int nin=0;
            int rad = (int)(cir.Radius+0.5f);
            int xc = (int)(cir.Center.X+0.5f);
            int yc = (int)(cir.Center.Y+0.5f);
            int i,j,radsq = rad*rad;
            for(i=-rad;i<=rad;i++)
                for(j=-rad;j<=rad;j++)
                    if( i*i+j*j<radsq )
                    {
                        ncir++;
                        if( PixelIsTagged(xc+i,yc+j) )
                            nin++;
                    }
            return (double)nin / (double)ncir;
        }
        /// <summary>
        /// Computes the union of this pixel tagger and the given circle by removing pixels lying outside the circle from this object.
        /// </summary>
        /// <param name="cir"></param>
        public void Union(Circle cir)
        {
            int l,r,t,b,i,j;
            l = (int)(cir.Center.X-cir.Radius);
            t = (int)(cir.Center.Y-cir.Radius);
            r = (int)(cir.Center.X+cir.Radius)+1;
            b = (int)(cir.Center.Y+cir.Radius)+1;
            //  Check to see if the circle's bounding rect is completely out of range
            if( Right<l ||  Left>r  || Top>b || Bottom<t )
            {
                tagged = new bool[0,0];
                NumPts = 0;
                return;
            }
            //  Find those too far left.
            if( Left<l )
            {
                for(i=Left; i<l; i++)
                    for(j=Top; j<=Bottom; j++)
                        RemovePixel(i,j);
            }
            //  Find those too far right.
            if( Right>r )
            {
                for(i=Right+1;i<=r;i++)
                    for(j=Top;j<=Bottom;j++)
                        RemovePixel(i,j);
            }
            //  Constrain limits on 'x'
            l = Math.Max(l,Left);
            r = Math.Min(r,Right);
            //  Find those too far above
            if( Top<t )
            {
                for(i=l; i<=r; i++)
                    for(j=Top; j<t; j++)
                        RemovePixel(i,j);
            }
            //  Find those too far below
            if(Bottom>b)
            {
                for(i=l;i<=r;i++)
                    for(j=b+1;j<=Bottom;j++)
                        RemovePixel(i,j);
            }
            //  Constrain limits on 'y'
            t = Math.Max(t,Top);
            b = Math.Min(b,Bottom);
            //  Compute union for the union rectangle
            double dx,dy;
            double radsq=cir.Radius*cir.Radius;
            for(i=l; i<=r; i++)
                for(j=t; j<=b; j++)
                    if( PixelIsTagged(i,j) )
                    {
                        dx = (float)i-cir.Center.X;
                        dy = (float)j-cir.Center.Y;
                        if( dx*dx+dy*dy>radsq )
                            RemovePixel(i,j);
                    }
        }
        public void RandomCirclesInPix(int radius, int nDesired, int nMaxTry, bool overlapping, out Circle[] result)
        {
            if (radius*radius > NumPts)
                result = new Circle[0];
            else
            {
                Circle cir;
                Random rand = ObjectParserData.Rand;
                int left = Left + radius;
                int right = Right - radius;
                int top = Top + radius;
                int bottom = Bottom - radius;
                RectangleF bf = RectangleF.FromLTRB(left, top, right, bottom);
                int radsq4 = radius*radius*4;
                ArrayList output = new ArrayList();
                int xc, yc, i, j, dx, dy;
                while(nDesired>0 && nMaxTry>0)
                {
                    nMaxTry--;
                    xc = (int)((float)rand.NextDouble()*bf.Width+bf.Left);
                    yc = (int)((float)rand.NextDouble()*bf.Height+bf.Top);
                    if(this.PixelIsTagged(xc,yc))
                    {
                        for(i=-radius; i<=radius; i++)
                            for(j=-radius; j<=radius; j++)
                                if(!this.PixelIsTagged(xc+i,yc+j))
                                    i=j=Int32.MaxValue-1;
                        if(i!=Int32.MaxValue && !overlapping)
                        {
                            for(i=0; i<output.Count; i++)
                            {
                                cir = (Circle)output[i];
                                dx = (int)cir.Center.X-xc;
                                dy = (int)cir.Center.Y-yc;
                                if( dx*dx+dy*dy < radsq4 )
                                    i = Int32.MaxValue-1;
                            }
                        }
                        if(i!=Int32.MaxValue)
                        {
                            output.Add(new Circle( new PointF((float)xc, (float)yc), radius, false ));
                            nDesired--;
                        }
                    }
                }
                result = (Circle[])output.ToArray(typeof(Circle));
            }
        }
        public PixelTagger Clone
        {
            get
            {
                PixelTagger output = new PixelTagger();
                output.Left = Left;
                output.Top = Top;
                output.Bottom = Bottom;
                output.Right = Right;
                output.NumPts = NumPts;
                int w,h,i,j;
                w = Right-Left+1;
                h = Bottom-Top+1;
                output.tagged = new bool[w,h];
                for(i=0; i<h; i++)
                    for(j=0; j<w; j++)
                        output.tagged[j,i] = tagged[j,i];
                return output;
            }
        }
    }
}
